﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace LSocket
{
    class ClientManager
    {
        public static int ReceiveBuffSize = 500000;
        public static int SendBuffSize = 500000;
        static EventWaitHandle waitHandle = new AutoResetEvent(false);
        internal static ConcurrentDictionary<Guid, LClient> dicClients = new ConcurrentDictionary<Guid, LClient>();
        static ConcurrentQueue<LMessage> ReceivedMessage = new ConcurrentQueue<LMessage>();

        static Thread CheckNewCmd;


        public static void Init() {
            CheckNewCmd = new Thread(DealCmdQueue);
            CheckNewCmd.IsBackground = true;
            CheckNewCmd.Start();
        }

        public static LClient AddClient(Socket sock) {
            LClient client = new LClient();
            client.ClientSock = sock;
            client.ReceiveSAEA = new SocketAsyncEventArgs();
            client.ReceiveSAEA.UserToken = client;
            client.ReceiveSAEA.RemoteEndPoint = sock.RemoteEndPoint;
            client.SendSAEA = new SocketAsyncEventArgs();
            client.SendSAEA.RemoteEndPoint = sock.RemoteEndPoint;
            client.SetReceiveBuffer(new byte[ReceiveBuffSize]);
            client.SetSendBuffer(new byte[SendBuffSize]);
            if (dicClients.TryAdd(client.ClientID, client)) {
                LSocketManager.CallOnAcceptClient(client);
            }
            else {
                client = null;
            }
            return client;
        }

        public static bool RemoveClient(Guid clientID) {
            bool successed = false;
            if (dicClients.ContainsKey(clientID)) {
                LClient client = null;
                successed = dicClients.TryRemove(clientID, out client);
                LSocketManager.CallOnClientClose(client);
                client.Dispose();
            }
            return successed;
        }

        internal static void EnqueueNewCmd(LClient client) {
            byte[] cmd = new byte[client.ReceiveSAEA.BytesTransferred];
            Array.Copy(client.ReceiveSAEA.Buffer, 0, cmd, 0, cmd.Length);
            LMessage message = new LMessage() {
                client = client,
                newCmd = cmd
            };
            ReceivedMessage.Enqueue(message);
            waitHandle.Set();
        }

        private static void DealCmdQueue() {
            LMessage message = null;
            while (!LSocketManager.isQuit) {
                waitHandle.WaitOne();
                if (ReceivedMessage.TryDequeue(out message)) {
                    LSocketManager.CallOnReceived(message);
                }
            }
        }
    }

    public class LClient : IDisposable
    {
        public Guid ClientID {  get; private set; }
        public string ClientName;

        internal Socket ClientSock;
        internal SocketAsyncEventArgs ReceiveSAEA;
        internal SocketAsyncEventArgs SendSAEA;
        //internal byte[] ReceiveBuffer { get; private set; }
        //internal byte[] SendBuffer { get; private set; }

        public bool isDispose = false;

        public LClient() {
            ClientID = Guid.NewGuid();
        }
        internal void SetReceiveBuffer(byte[] buffer) {
            //ReceiveBuffer = buffer;
            ReceiveSAEA.SetBuffer(buffer, 0, buffer.Length);
        }

        internal void SetSendBuffer(byte[] buffer) {
            //SendBuffer = buffer;
            SendSAEA.SetBuffer(buffer, 0, buffer.Length);
            SendSAEA.Completed += SendSAEA_Completed;
        }

        private void SendSAEA_Completed(object sender, SocketAsyncEventArgs e) {
            Console.WriteLine("发送成功！");
        }

        public void Send(byte[] cmd) {
            Array.Clear(SendSAEA.Buffer, 0, SendSAEA.Buffer.Length);
            cmd.CopyTo(SendSAEA.Buffer, 0);
            SendSAEA.SetBuffer(0, cmd.Length);
            bool willRaiseEvent = ClientSock.SendAsync(SendSAEA);
            if (!willRaiseEvent) {
                SendSAEA_Completed(null, SendSAEA);
            }
        }

        public override string ToString() {
            return ReceiveSAEA.RemoteEndPoint.ToString();
        }

        public void Dispose() {
            if (isDispose)
                return;
            ClientSock.Close();
            SendSAEA.Dispose();
            ReceiveSAEA.Dispose();

            isDispose = true;
        }
    }

    public class LMessage
    {
        public LClient client;
        public byte[] newCmd;
    }

}
